package com.itheima.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.itheima.domain.dto.ScrollResult;
import com.itheima.domain.dto.UserDTO;
import com.itheima.domain.entity.Blog;
import com.itheima.domain.entity.Follow;
import com.itheima.domain.entity.User;
import com.itheima.domain.vo.Result;
import com.itheima.mapper.BlogMapper;
import com.itheima.service.IBlogService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.itheima.service.IFollowService;
import com.itheima.service.IUserService;
import com.itheima.utils.ThreadUtil;
import jakarta.annotation.Resource;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author 坤哥
 * @since 2025-10-06
 */
@Service
public class BlogServiceImpl extends ServiceImpl<BlogMapper, Blog> implements IBlogService {
    @Resource
    private StringRedisTemplate stringRedisTemplate;
    @Resource
    private IUserService userService;
    @Resource
    private IFollowService followService;

    /**
     * 查询热门博客列表
     */
    @Override
    public List<Blog> getHotBlogs(Long current) {
        Page<Blog> blogPage = new Page<>(current,10);
        LambdaQueryWrapper<Blog> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.orderByDesc(Blog::getLiked);
        Page<Blog> page = page(blogPage, queryWrapper);
        List<Blog> blogList = page.getRecords();
        //查询登录的用户是否给博客点赞
        checkUserIsLiked(blogList);
        //封装博客作者的信息
        return setBlogUserInfo(blogList);
    }

    /**
     * 封装博客作者的信息
     */
    private List<Blog> setBlogUserInfo(List<Blog> blogList) {
        return blogList.stream().map(blog -> {
            User user = userService.lambdaQuery().eq(User::getId, blog.getUserId()).one();
            blog.setName(user.getNickName());
            blog.setIcon(user.getIcon());
            return blog;
        }).collect(Collectors.toList());
    }

    /**
     * 查询登录的用户是否给博客点赞
     */
    private void checkUserIsLiked(List<Blog> blogList) {
        Long userId = ThreadUtil.get().getUserId();
        for (Blog blog : blogList) {
            Double score = stringRedisTemplate.opsForZSet().score("blog:like:" + blog.getId(), userId.toString());
            blog.setIsLike(score != null);
        }
    }

    /**
     * 为某篇博客点赞
     */
    @Override
    public void likeOneBlog(Long blogId) {
        //获取用户信息
        Long userId = ThreadUtil.get().getUserId();
        //判断作品的点赞用户中是否包含该点赞用户
        Double score = stringRedisTemplate.opsForZSet().score("blog:like:" + blogId, userId.toString());
        //不包含就是点赞
        if(score == null){//点赞
            lambdaUpdate().eq(Blog::getId,blogId).setSql("liked = liked + 1").update();
            stringRedisTemplate.opsForZSet().add("blog:like:" + blogId, userId.toString(),System.currentTimeMillis());
        }else { //包含就是取消点赞
            lambdaUpdate().eq(Blog::getId,blogId).setSql("liked = liked - 1").update();
            stringRedisTemplate.opsForZSet().remove("blog:like:" + blogId, userId.toString());
        }
    }
    /**
     * 查询某个博客的主页
     */
    @Override
    public Result queryBlogInfo(Long blogId) {
        Blog blog = lambdaQuery().eq(Blog::getId, blogId).one();
        return Result.ok(blog);
    }
    /**
     * 查询某个博客点赞排名前五的用户
     */
    @Override
    public Result queryBlogLikesTop5(Long blogId) {
        Set<String> userIds = stringRedisTemplate.opsForZSet().range("blog:like:" + blogId, 0, 4);
        if(CollectionUtil.isEmpty(userIds)){//没人点赞就返回一个空集合
            return Result.ok(Collections.emptyList());
        }
        //根据userIds的顺序查询用户信息
        List<Long> userIdList = userIds.stream().map(Long::valueOf).toList();
        String strIds = StrUtil.join(",", userIds);
        List<User> users = userService.lambdaQuery().in(User::getId, userIdList).last("order by field(id," + strIds + ")").list();
        List<UserDTO> userDTOS = users.stream().map(user -> {
            return BeanUtil.copyProperties(user, UserDTO.class);
        }).collect(Collectors.toList());
        return Result.ok(userDTOS);
    }

    /**
     * 查询自己关注博主的博客
     */
    @Override
    public Result queryMyFollowBlog(Long max, Integer offset) {
        //获取当前用户id
        Long userId = ThreadUtil.get().getUserId();
        //去自己的邮箱中查询自己关注博主的博客--zrangebyscore key min max  -- 应该用倒序，因为最新的信息要排在前面
        String key = "fan:mail:" + userId;
        Set<ZSetOperations.TypedTuple<String>> typedTuples = stringRedisTemplate.opsForZSet()
                .reverseRangeByScoreWithScores(key, 0, max, offset, 2);//默认查两篇博客
        //判断集合是否为空，若为空说明没人发博客，返回空集合
        if(CollectionUtil.isEmpty(typedTuples)){
            return Result.ok(Collections.emptyList());
        }
        //当滚动查询时，会计算接下来要查询的最大时间戳和偏移量
        //拿到集合的时间戳后会遍历score，看下是否有相同的时间戳，若有则偏移量++
        long minTime = 0;
        Integer count = 1;
        List<String> blogs = new ArrayList<>();
        //拿到博客的id后会去查询博客的详细信息，然后和最大时间戳（minTime）、偏移量一并返回
        for (ZSetOperations.TypedTuple<String> typedTuple : typedTuples) {
            //从中取出blogId并保存
            String blogId = typedTuple.getValue();
            blogs.add(blogId);
            //计算最大时间戳（minTime）、偏移量
            long score = typedTuple.getScore().longValue();
            if(score == minTime){
                count++;
            }else {
                minTime = score;
                count = 1;
            }
        }
        //把blogIds拼接成字符串
        String strBlogs = StrUtil.join(",", blogs);
        //按顺序查询博客的详细信息
        List<Blog> blogList = lambdaQuery().in(Blog::getId, blogs).last("order by field(id," + strBlogs + ")").list();
        //把所需的信息封装并返回
        ScrollResult scrollResult = ScrollResult.builder()
                .list(blogList)
                .minTime(minTime)
                .offset(count)
                .build();
        return Result.ok(scrollResult);
    }
    /**
     * 发布探店笔记
     */
    @Override
    public Result sendBlogWithShop(Blog blog) {
        //获取当前用户id
        Long userId = ThreadUtil.get().getUserId();
        blog.setUserId(userId);
        boolean success = save(blog);
        if(!success){
            return Result.fail("发布博客失败");
        }
        //发送成功后去数据库中查看自己的粉丝是谁
        List<Follow> follows = followService.lambdaQuery().eq(Follow::getFollowUserId, userId).list();
        //拿到粉丝们的id
        List<Long> userIds = follows.stream().map(Follow::getUserId).toList();
        //然后把博客推送到粉丝的邮箱
        for (Long id : userIds) {
            String key = "fan:mail:" + id;
            stringRedisTemplate.opsForZSet().add(key,blog.getId().toString(),System.currentTimeMillis());
        }
        return Result.ok(blog);
    }
}
